#pragma once

#include "ConstantsBuffer.hpp"
#include "D3D11Declarations.hpp"




namespace Engine
{
	namespace Video
	{
		class D3D11ConstantsBuffer
			: public ConstantsBuffer
		{
		public:

			SHARED(D3D11ConstantsBuffer);
			typedef boost::function<void (weak_ptr)>   notify;

		protected:

			D3D11ConstantsBuffer(const notify& destroyedFn, uint32 dataSize, const StaticReflection& bufferReflection, Buffer::Type usageType);
			void post_create(ptr _this);

		public:

			MAKE_SHARED4(D3D11ConstantsBuffer, notify, uint32, StaticReflection, Buffer::Type);
			~D3D11ConstantsBuffer();



			/**
			 * Get the usage mode of this buffer.
			 * Defines if this buffer is static (ie. may not changed),
			 * SemiDynamic (may be changed, but doesn't happen frequently) or
			 * Dynamic (may be changed, and happens often).
			 */
			Buffer::Type usage() const;

			/**
			 * Get the Layout of this buffer.
			 * The layout defines how this buffer is laid out in memory.
			 */
			StaticReflection reflect() const;



			/**
			 * Applies the buffer (ie. creates/updates it, but only when required)
			 * and returns a pointer to the D3D11 interface to give it to the pipeline.
			 */
			const ID3D11BufferPtr& apply(const ID3D11DevicePtr& device, const ID3D11DeviceContextPtr& context);

		protected:

			/**
			 * Update the ConstantsBuffer with new data.
			 *
			 * @param data         pointer to the actual data
			 * @param dataSize     size of the entire buffer (thus required size of the buffer)
			 */
			void update(const void* data, uint32 dataSize);

			/**
			 * Updates the actual GPU buffer with new data.
			 */
			void updateBuffer(const ID3D11DevicePtr& device, const ID3D11DeviceContextPtr& context);

			/**
			 * Get a pointer to the beginning of the buffer.
			 * The size of the requested data must be given in order to
			 * quickly spot errors.
			 */
			void* data(uint32 dataSize);

			/**
			 * Get the D3D11_USAGE enum identifier that describes the given buffer usage.
			 */
			static D3D11_USAGE usageFromType(Buffer::Type usageType);

			/**
			 * Get the cpu access flags for the given buffer usage.
			 */
			static UINT cpuAccessFlags(Buffer::Type usageType);

		private:

			weak_ptr            m_this;
			notify              m_destroyed;

			bool                m_changed;

			StaticReflection    m_bufferReflection;
			Buffer::Type        m_usageType;

			ID3D11BufferPtr     m_buffer;
			uint32              m_bufferSize;
			const uint32        m_size;

			std::vector<byte>   m_tmpBuffer;
		};
		///////////////////////////////////////////////////////////////////////////////////////////
	}
	///////////////////////////////////////////////////////////////////////////////////////////////
}
///////////////////////////////////////////////////////////////////////////////////////////////////
